// Encapsulates the behavior of a single slot machine reel capable of displaying 3 symbols at a time.
class Reel {
  
    SlotMachine parent;
    PImage reelWindow;
    
    SlotSymbol[] symbols;
    
    // the current symbol in the middle of the slot machine
    int currentSymbol;

    // the current valocity of this wheel in pixels per second
    float currentVelocity;
    float deceleration;
    float position;
    
    int lastMovement;
    float finalPosition;

    Reel(SlotMachine parent, int startingSymbol) {
      this.parent = parent;
      this.currentSymbol = startingSymbol;

      // the starting position is the pixel location of the symbol above the selected symbol
      this.position = ((startingSymbol-1) % parent.getSymbolCount()) * parent.getSymbolHeightPx();
      generateReel();
    }
    
    void generateReel() {
      symbols = new SlotSymbol[parent.getSymbolCount()];
      for(int i = 0; i < symbols.length; i++) {
        symbols[i] = parent.getSymbol(i);
      }
      // shuffle the symbols
      Collections.shuffle(Arrays.asList(symbols));
      
      // build the reel image  
      reelWindow = createImage(16,160,RGB);
      for(int i = 0; i < symbols.length; i++)
        reelWindow.set(0,i*16,symbols[i].getImg());
    }
    
    int getCurrentSymbolId() {
      println(getCurrentSymbolIndex());
      return symbols[getCurrentSymbolIndex()].getId();
    }
    int getCurrentSymbolIndex() {
      return (pixelPosition()/parent.getSymbolHeightPx() + 1) % symbols.length;       
    }
    
    int distanceToSymbol(int symbolId) {
      int currentSymbolIndex = getCurrentSymbolIndex();
      for(int i = 0; i < parent.getSymbolCount(); i++)
        if(symbols[(i + currentSymbolIndex) % symbols.length].getId() == symbolId)
          return i;
      return 0;
    }
    // spins this reel the specified number of symbols
    void spin(int symbolCount) {
      
      // wonky way to prevent overflows while keeping the update math mostly free of modulo.
      position = pixelPosition();
      currentVelocity = INITIAL_VELOCITY;
      deceleration = ((float)(symbolCount * parent.getSymbolHeightPx()) - (float)currentVelocity * SPIN_DURATION)/(.5*SPIN_DURATION*SPIN_DURATION);
      finalPosition = position + symbolCount * parent.getSymbolHeightPx() + (getCurrentSymbolIndex() * parent.getSymbolHeightPx() - (position+parent.getSymbolHeightPx()));
      lastMovement = millis();
    }
    
    
    void spinIndefinite(int velocity) {
      // wonky way to prevent overflows while keeping the update math mostly free of modulo.
      position = pixelPosition();
      currentVelocity = velocity;
      deceleration = 0;
      finalPosition = -1;
      lastMovement = millis();
    }
 
    void update() {
        int currentTime = millis();
        if(currentTime != lastMovement) {
          // if we're close enough to the final position, or if we have gone too far, set the position directly
            
          if(finalPosition > -1 && (finalPosition - position) < 2) {
            position = finalPosition;
            currentVelocity = 0;
          }          // otherwise, interpolate the position
          else {
            
            float elapsedSeconds = ((float)currentTime - (float)lastMovement)/1000.0f;
            if(elapsedSeconds > 0) {
              // update position
              float positionChange = (currentVelocity*elapsedSeconds) + (.5f * deceleration * elapsedSeconds * elapsedSeconds);
  
              position += positionChange;
              
              // update velocity
              currentVelocity += deceleration * elapsedSeconds;
              lastMovement = currentTime;
            }
          }
        }
    }
       
    PImage getReelWindow() {
      return reelWindow;
    }
    
    int pixelPosition () {
      return ((int)position) % parent.getImageHeight();
    }    
    
    boolean getIsSpinning() { return currentVelocity != 0; }
}
